﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media.Imaging;
using System.Windows.Media.Animation;
using System.Windows.Navigation;
using BTM_Client_Application.Chat;
using BTM_Client_DBwork;
using BTM_Client_Library;
using BTM_Client_Application.SupportClasses;
using BTM_Client_DBwork.Constants;
using System.Net;
using Client_Server_Interface;

namespace BTM_Client_Application
{
    /// <summary>
    /// Логика взаимодействия для LogInWindow.xaml
    /// </summary>
    public partial class LogInWindow
    {
        private const int CollapsedWidth = 350;
        private const int ExpandedWidth = 570;
        private ButtonStatusSetter _buttonStatusSetter;

        private static bool _absolutelyActive = true;
        private bool _expanded;
        private DBwork _dbwork;
        private Dictionary<string, string> _langList;

        private const string IdFileName = "clientid";
        //public  RoutedCommand MyCommand = new RoutedCommand();

        private class ButtonStatusSetter
        {
            private readonly Button _button;
            private bool _login;
            private bool _passwod;
            private bool _ip;

            public ButtonStatusSetter(Button but)
            {
                _button = but;
            }
            public bool Login
            {
                private get { return _login; }
                set
                {
                    _login = value;
                    Determine();
                }
            }
            public bool Password
            {
                private get { return _passwod; }
                set
                {
                    _passwod = value;
                    Determine();
                }
            }
            public bool Ip
            {
                private get { return _ip; }
                set
                {
                    _ip = value;
                    Determine();
                }
            }

            private void Determine()
            {
                if (Login && Password && Ip && _absolutelyActive)
                    _button.IsEnabled = true;
                else
                {
                    _button.IsEnabled = false;
                }
            }
        }

        public LogInWindow()
        {
            InitializeComponent();
            SoundsWorker.NewChatMessagePlay();
        }

        private void AcceptButton_Click(object sender, RoutedEventArgs e)
        {
            if (!AcceptButton.IsEnabled)
                return;
            SwitchAcceptButton(false);
            AliveInfo.AddStatus(ResourceNames.CONNECTING);
            AliveInfo.RemoveStatus(ResourceNames.CAN_NOT_CONNECT_TO_SERVER);
            AliveInfo.RemoveStatus(ResourceNames.AUTHENTICATION_ERROR);
            var loginTask = new Task(Action);
            loginTask.Start();
            
        }

        //Log in function
        private void Action()
        {
            CheckMapCacheLocation();
            UpdateOptionsInDb();
            try
            {
                ConnectToServer();
            }
            catch (ServerRequestException)
            {
                AliveInfo.AddStatus(ResourceNames.CAN_NOT_CONNECT_TO_SERVER);
                SwitchAcceptButton();
                return;
            }
            finally
            {
                AliveInfo.RemoveStatus(ResourceNames.CONNECTING);
            }
            AliveInfo.AddStatus(ResourceNames.AUTHENTICATION);
            AliveInfo.RemoveStatus(ResourceNames.CAN_NOT_CONNECT_TO_SERVER);
            
            //var clid = File.ReadAllBytes(IdFileName);
            //var clid = File.ReadAllBytes("c:\\clientid");
            //var idFile = Res.Rm.GetStream("clientid");
            var clid = BTM_Client_Application.Properties.Resources.clientid;
            
            //Можно получить пустое дерево
            string login = "";
            string password = "";
            Dispatcher.Invoke(new VoidDelegate(() =>
                {
                    login = LoginBox.Text;
                    password = PasswordBox.Password;
                }));
            try
            {
                //Получаем дерево
                //Errors: Пустое дерево
                
                AliveInfo.DeviceTree = AliveInfo.ClientHelper.LoginToServer_GetTree(login, password, clid);
                
                if (AliveInfo.DeviceTree.DeviceTreeItems.Count < 1)
                {
                    MessageBox.Show(Res.Rm.GetString("RetryAction"));
                    SwitchAcceptButton();
                    return;
                }

                AliveInfo.SetLoginData(login, password, clid);
            }
            catch (Exception ex)
            {
                AliveInfo.ClientHelper.Disconnect();
                Dispatcher.Invoke(new VoidDelegate(() =>
                    {
                        StatusBox.Text = Res.Rm.GetString(ResourceNames.AUTHENTICATION_ERROR);
                    }));
                if (ex.GetType() == typeof (ServerRequestException))
                {
                    var srex = (ServerRequestException) ex;
                    var str = Res.Rm.GetString(ex.Message, AliveInfo.CurrentCulture);
                    var accDenStr = Res.Rm.GetString("AuthenticationError", AliveInfo.CurrentCulture);
                    if (srex.Subject == ServerRequestException.ExceptionSubject.Error)
                    {
                        MessageBox.Show(str ?? (Res.Rm.GetString("UnsuccessfulConnectionMessage", AliveInfo.CurrentCulture) + ".\r\n" + Res.Rm.GetString("RetryAction", AliveInfo.CurrentCulture)) + '.', accDenStr, MessageBoxButton.OK, MessageBoxImage.Error);
                    }
                    else if (srex.Subject == ServerRequestException.ExceptionSubject.AccessDenied)
                    {
                        switch (ex.Message)
                        {
                            case ServerResponceTypes.LOGIN_IS_NOT_UNIQUE:
                                MessageBox.Show(str ?? Res.Rm.GetString("LoginIsNotUniqueMessage", AliveInfo.CurrentCulture) + '.', accDenStr);
                                break;
                            case ServerResponceTypes.WRONG_VERSION:
                                MessageBox.Show(str ?? Res.Rm.GetString("WrongVersionMessage", AliveInfo.CurrentCulture) + '.', accDenStr);
                                break;
                            case ServerResponceTypes.CONNECTIONS_LIMIT:
                                MessageBox.Show(str ?? Res.Rm.GetString("ConnectionsLimitMessage", AliveInfo.CurrentCulture) + '.', accDenStr);
                                break;
                            case ServerResponceTypes.WRONG_LOGIN:
                                MessageBox.Show(str ?? Res.Rm.GetString("WrongLoginMessage", AliveInfo.CurrentCulture) + '.', accDenStr);
                                break;
                            case ServerResponceTypes.UNKNOWN_ADDRESS:
                                MessageBox.Show(str ?? Res.Rm.GetString("WrongAddressMessage", AliveInfo.CurrentCulture) + '.', accDenStr);
                                break;
                            case ServerResponceTypes.UNKNOWN_CLIENT_ID:
                                MessageBox.Show(str ?? Res.Rm.GetString("UnknownClientIdMessage", AliveInfo.CurrentCulture) + '.', accDenStr);
                                break;
                        }
                    }
                    
                }
                else
                    MessageBox.Show(ex.Message, Res.Rm.GetString("Warning", AliveInfo.CurrentCulture));
                SwitchAcceptButton();
                return;
            }
            AliveInfo.RemoveStatus(ResourceNames.AUTHENTICATION);
            AliveInfo.AddStatus(ResourceNames.LOADING);
            LoginComplete();
        }

        private void CheckMapCacheLocation()
        {
            try
            {
                string mapPath = AliveInfo.DBwork.GetParameterValue(OptionNames.MAP_CACHE_LOCATION);                
                if (!File.Exists(mapPath + Res.DbCacheInFolderLocation))
                {
                    var res =
                        MessageBox.Show(Res.Rm.GetString("CanNotConnectToServer", AliveInfo.CurrentCulture) + ".\r\n" +
                                        Res.Rm.GetString("WouldULikeToDoItNow", AliveInfo.CurrentCulture) + '?',
                                        Res.Rm.GetString("Warning", AliveInfo.CurrentCulture), MessageBoxButton.YesNo,
                                        MessageBoxImage.Question);
                    if (res == MessageBoxResult.Yes)
                    {
                        SelectCachelocation();
                    }
                }
            }
            catch (Exception ex)
            {
                var res =
                    MessageBox.Show(Res.Rm.GetString("MapCacheLocationNotSet", AliveInfo.CurrentCulture) + ".\r\n" +
                                    Res.Rm.GetString("WouldULikeToDoItNow", AliveInfo.CurrentCulture) + '?',
                                    Res.Rm.GetString("Warning", AliveInfo.CurrentCulture), MessageBoxButton.YesNo,
                                    MessageBoxImage.Question);
                
                if (res == MessageBoxResult.Yes)
                {
                    SelectCachelocation();
                }
            }
        }

        private void SelectCachelocation()
        {
            var d = new MyOpenFileDialog();
            try
            {
                var fname = d.Show();
                Environment.CurrentDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
                var fi = new FileInfo(fname);
                var rightDir = fi.Directory.Parent.Parent.FullName;
                bool chk = false;
                Dispatcher.Invoke(new Action(() =>
                    {
                        chk = AliveInfo.DBwork.SetParameter(OptionNames.MAP_CACHE_LOCATION, rightDir);
                    }));
                if (!chk)
                {
                    MessageBox.Show("Saving map path error.", Res.Rm.GetString("Warning"));
                }
            }
            catch (IncorrectInputValueException)
            {
                
            }
        }

        private void UpdateOptionsInDb()
        {
            Dispatcher.Invoke(new VoidDelegate(() =>
                {
                    _dbwork.SetParameter(OptionNames.SAVED_LOGIN, SaveLoginBox.IsChecked == true ? LoginBox.Text : "");
                    _dbwork.SetParameter(OptionNames.UI_LANGUAGE, AliveInfo.CurrentCulture.ToString());
                    _dbwork.SetParameter(OptionNames.NEED_SAVE_LOGIN, SaveLoginBox.IsChecked.ToString());
                    _dbwork.SetParameter(OptionNames.SERVER_IP, IpBox.Text);
                }));

        }

        private void LoginComplete()
        {
            //LoadingWindow.CloseWindow();
            Dispatcher.Invoke(new VoidDelegate(() =>
            {
                Hide();
                LoadingWindow.ShowWindow();
                AliveInfo.ResetStatusUpdateEvent();
                var m = new MainWindow();
                m.Show();
                LoadingWindow.CloseWindow();
                Close();
            }));
        }

        /*private void CreateLoadWindow()
        {
            var thread = new Thread(() =>
            {
                _loadWindow = new LoadingWindow();
                _loadWindow.Show();

                _loadWindow.Closed += (sender2, e2) =>
                    _loadWindow.Dispatcher.InvokeShutdown();

                System.Windows.Threading.Dispatcher.Run();
            });
            thread.SetApartmentState(ApartmentState.STA);
            thread.Start();
        }

        private void CloseLoadWindow()
        {
            _loadWindow.Dispatcher.Invoke(new VoidDelegate(() => _loadWindow.Close()));
        }*/

        private void ConnectToServer()
        {
            var ip = "1.1.1.1";
            Dispatcher.Invoke(new VoidDelegate(() => { ip = IpBox.Text; }));
            if (AliveInfo.ClientHelper == null)
            {
                //var ip = "1.1.1.1";
                AliveInfo.ClientHelper = new ClientHelper(IPAddress.Parse(ip));
            }
            AliveInfo.ClientHelper.SetIp = IPAddress.Parse(ip);
            AliveInfo.ClientHelper.Connect(AliveInfo.OnServerDisconnectEvent);
        }

        private void CancelButton_Click(object sender, RoutedEventArgs e)
        {
            Application.Current.Shutdown();
        }

        private void LogInWindow_OnInitialized(object sender, EventArgs e)
        {
            //AliveInfo.ClientHelper = new ClientHelper(IPAddress.Parse("127.0.0.1"));
            //AliveInfo.Chat = new ChatClient("228");
            //var w = new Window();
            //w.Content = new ChatControl();
            //w.Show();

            var myCommand1 = new RoutedCommand();
            CommandBindings.Add(new CommandBinding(myCommand1, AcceptButton_Click));
            myCommand1.InputGestures.Add(new KeyGesture(Key.Enter));

            var myCommand2 = new RoutedCommand();
            CommandBindings.Add(new CommandBinding(myCommand2, CancelButton_Click));
            myCommand2.InputGestures.Add(new KeyGesture(Key.Escape));

            AcceptButton.IsEnabled = false;
            Width = CollapsedWidth;
            Icon = (BitmapImage)FindResource("MonitorToDark");
            //Список доступных языков (Имя ресурса, название культуры)
            _langList = new Dictionary<string, string> {{"Russian", "ru"}, {"English", "en"}};

            //_dbwork = new DBwork();
            AliveInfo.DBwork = new DBwork();
            _dbwork = AliveInfo.DBwork;
            _buttonStatusSetter = new ButtonStatusSetter(AcceptButton);
            PasswordBox.PasswordChanged += PasswordBox_OnPasswordChanged;
            PasswordBox_OnPasswordChanged(PasswordBox, null);
            AliveInfo.StatusUpdateEvent += status => Dispatcher.Invoke(new VoidDelegate(() =>
                {
                    StatusBox.Text = Res.Rm.GetString(status, AliveInfo.CurrentCulture);
                }));

            //assembly info
            Assembly assembly = Assembly.GetExecutingAssembly();
            FileVersionInfo fvi = FileVersionInfo.GetVersionInfo(assembly.Location);
            ProgramNameBox.Text = fvi.ProductName + " v. " + fvi.ProductVersion;
            WebSiteBox.Text = fvi.CompanyName;

            //Получаем язык интерфейса из БД
            try
            {
                var culture = _dbwork.GetParameterValue(OptionNames.UI_LANGUAGE);
                AliveInfo.CurrentCulture = CultureInfo.GetCultureInfo(culture);
            }
            catch (Exception)
            {
                //Если в базе нет этого параметра, то добавляем его и устанавливаем язык по умолчанию
                _dbwork.SetParameter(OptionNames.UI_LANGUAGE, AliveInfo.CurrentCulture.ToString());
            }
            
            UiLanguageChanged();
            LanguageBox.SelectionChanged += LanguageBoxOnSelectionChanged;

            //Получение из базы имени пользователя
            try
            {
                var login = _dbwork.GetParameterValue(OptionNames.SAVED_LOGIN);
                LoginBox.Text = login;
            }
            catch (Exception)
            {
                _buttonStatusSetter.Login = false;
                AliveInfo.AddStatus(ResourceNames.INPUT_LOGIN);
            }

            //Получение из базы состояния чекбокса
            try
            {
                var saveLogin = _dbwork.GetParameterValue(OptionNames.NEED_SAVE_LOGIN);
                SaveLoginBox.IsChecked = bool.Parse(saveLogin);
            }
            catch (Exception)
            {
                SaveLoginBox.IsChecked = false;
            }

            //Получение сохранненого в базе Ip сервера
            try
            {
                var ip = _dbwork.GetParameterValue(OptionNames.SERVER_IP);
                IpBox.Text = ip;
                AliveInfo.RemoveStatus(ResourceNames.INCORRECT_IP);
                _buttonStatusSetter.Ip = true;
                //ConnectToServer();
                
            }
            catch (Exception)
            {
                //Если Ip в базе не найден
                _buttonStatusSetter.Ip = false;
                ExpandOptions();
                AliveInfo.AddStatus(ResourceNames.INCORRECT_IP);
            }
            AliveInfo.AddStatus(ResourceNames.INPUT_PASSWORD);
        }

        private void LanguageBoxOnSelectionChanged(object sender, SelectionChangedEventArgs selectionChangedEventArgs)
        {
            LanguageBox.SelectionChanged -= LanguageBoxOnSelectionChanged;
            if (LanguageBox.SelectedItem != null)
                AliveInfo.CurrentCulture = new CultureInfo(((ComboBoxItem)(((ComboBox)sender).SelectedItem)).Tag.ToString());
            UiLanguageChanged();
            LanguageBox.SelectionChanged += LanguageBoxOnSelectionChanged;
        }

        private void ExpandButton_Click(object sender, RoutedEventArgs e)
        {
            if (!_expanded)
            {
                ExpandOptions();
            }
            else
            {
                CollapseOptions();
            }
        }
        
        private void ExpandOptions()
        {
            var a = new DoubleAnimation(CollapsedWidth, ExpandedWidth, new Duration(new TimeSpan(0, 0, 0, 0, 100)));
            BeginAnimation(WidthProperty, a);
            _expanded = !_expanded;
            ExpandImage.Source = (BitmapImage)FindResource("NavigatePreviousImageLight");
        }

        private void CollapseOptions()
        {
            var a = new DoubleAnimation(ExpandedWidth, CollapsedWidth, new Duration(new TimeSpan(0, 0, 0, 0, 100)));
            BeginAnimation(WidthProperty, a);
            _expanded = !_expanded;
            ExpandImage.Source = (BitmapImage)FindResource("NavigateNextImageLight");
        }

        private void UiLanguageChanged() //Обновление языка интерфейса
        {
            LoginLabel.Content = Res.Rm.GetString("Login", AliveInfo.CurrentCulture);
            PasswordLabel.Content = Res.Rm.GetString("Password", AliveInfo.CurrentCulture);
            IpLabel.Content = Res.Rm.GetString("Ip", AliveInfo.CurrentCulture);
            //StatusLabel.Text = Res.Rm.GetString("Status", AliveInfo.CurrentCulture);
            LanguageLabel.Content = Res.Rm.GetString("Language", AliveInfo.CurrentCulture);
            AcceptButton.Content = Res.Rm.GetString("Enter", AliveInfo.CurrentCulture);
            CancelButton.Content = Res.Rm.GetString("Exit", AliveInfo.CurrentCulture);
            Title = Res.Rm.GetString("LoginWindowTitle", AliveInfo.CurrentCulture);
            SaveLoginBox.Content = Res.Rm.GetString("SaveLogin", AliveInfo.CurrentCulture);
            StatusBox.Text = Res.Rm.GetString(AliveInfo.Status, AliveInfo.CurrentCulture);
            OptionsLabel.Content = Res.Rm.GetString("Options", AliveInfo.CurrentCulture);
            LanguageBox.Items.Clear();
            foreach (var lang in _langList)
            {
                var item = new ComboBoxItem
                    {
                        Content = Res.Rm.GetString(lang.Key, AliveInfo.CurrentCulture),
                        Tag = lang.Value
                    };

                LanguageBox.Items.Add(item);
                if (lang.Value == AliveInfo.CurrentCulture.ToString())
                    LanguageBox.SelectedItem = item;
            }
        }

        private void IpBox_OnTextChanged(object sender, TextChangedEventArgs e)
        {
            if (Res.CheckIp(IpBox.Text))
            {
                AliveInfo.RemoveStatus(ResourceNames.INCORRECT_IP);
                _buttonStatusSetter.Ip = true;
            }
            else
            {
                _buttonStatusSetter.Ip = false;
                AliveInfo.AddStatus(ResourceNames.INCORRECT_IP);
            }
        }

        private void LoginBox_OnTextChanged(object sender, TextChangedEventArgs e)
        {
            if (((TextBox) sender).Text.Length < 1)
            {
                _buttonStatusSetter.Login = false;
                AliveInfo.AddStatus(ResourceNames.INPUT_LOGIN);
            }
            else
            {
                _buttonStatusSetter.Login = true;
                AliveInfo.RemoveStatus(ResourceNames.INPUT_LOGIN);
            }
        }

        private void PasswordBox_OnPasswordChanged(object sender, RoutedEventArgs e)
        {
            var box = (PasswordBox) sender;
            if (box.Password.Length < 1)
            {
                _buttonStatusSetter.Password = false;
                AliveInfo.AddStatus(ResourceNames.INPUT_PASSWORD);
            }
            else
            {
                _buttonStatusSetter.Password = true;
                AliveInfo.RemoveStatus(ResourceNames.INPUT_PASSWORD);
            }

        }

        private void SwitchAcceptButton(bool? activate = null)
        {
            Dispatcher.Invoke(new VoidDelegate(() =>
                {
                    if (activate == null)
                    {
                        AcceptButton.IsEnabled = !AcceptButton.IsEnabled;
                        _absolutelyActive = AcceptButton.IsEnabled;
                    }
                    else
                    {
                        _absolutelyActive = AcceptButton.IsEnabled = activate == true;
                    }
                }));
        }

        private void WebSiteLink_OnRequestNavigate(object sender, RequestNavigateEventArgs e)
        {
            Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri));
            e.Handled = true;
        }
    }
}
